#pragma once


BEGIN_NAMESPACE_OXGE


template <typename T> 
class MATHLIB_TRANSFER Vector3T
{
public:
	typedef T ValueType;
	typedef Vector2T<T> Vector2Type;
	typedef Vector3T<T> VectorType;

	Vector3T(){  }
	Vector3T(T x, T y ,T z){ Set(x,y,z); }
	Vector3T(const Vector2Type& vec2){ Set(vec2.x, vec2.y, 0); }
	Vector3T(const T* vArray){ Set(vArray); }

	inline VectorType& Set(T ax, T ay ,T az){ x = ax; y = ay; z = az; return *this; } 
	inline VectorType& Set(const T* cv){ memcpy(c_v, cv, sizeof(T)*NLen); return *this; }

	VectorType& operator *= (const T& dScal ){ x*=dScal; y*=dScal; z*=dScal; return *this;}	
	VectorType& operator += (const VectorType& vR ){ x+=vR.x; y+=vR.y; z+=vR.z; return *this; }
	VectorType& operator -= (const VectorType& vR ){ x-=vR.x; y-=vR.y; z-=vR.z; return *this; }

	VectorType operator + (const VectorType& vR)const { return VectorType(x+vR.x,y+vR.y,z+vR.z); }
	VectorType operator - (const VectorType& vR)const { return VectorType(x-vR.x,y-vR.y,z-vR.z); }
	VectorType operator * (const T& rVal )const { return VectorType(x*rVal,y*rVal,z*rVal); } 

	friend VectorType operator *(const T& lVal, const VectorType& rVec){	return rVec * lVal; }
	////
	//T& operator[](size_t idx){ 
	//	assert(idx < NLen);
	//	return c_v[idx];
	//}
	////
	//const T& operator[](size_t idx)const{ 
	//	assert(idx < NLen);
	//	return c_v[idx];
	//}
	inline operator ValueType*() { return c_v; }	
	inline operator const ValueType*()const { return c_v; }

	//dot 
	inline T dot(const VectorType& vectR)const
	{
		const VectorType& vectL = *this;
		return vectL.x * vectR.x + vectL.y * vectR.y + vectL.z * vectR.z;
	}

	//cross
	VectorType cross(const VectorType& vectR)const
	{
		return VectorType( y*vectR.z - z*vectR.y,
						   z*vectR.x - x*vectR.z,
						   x*vectR.y - y*vectR.x);
	}

	//normalize
	inline VectorType& Normalize(){	return  SetLength(T(1)); }
	//square of len
	inline T SquareLength()const{ return x*x+y*y+z*z; }
	//length
	inline T Length()const{ return sqrt(SquareLength()); }
	//
	inline VectorType& SetLength(const T& len){	return (*this)*=(len/Length());	}
	//
	inline VectorType GetNormVector()const{ 
		VectorType vec = *this;
		return vec.Normalize();
	}	

	//unit test
	static void UnitTest();
public:

	const static size_t NLen = 3;
	
	union{
		struct{
			T x,y,z;
		};
		T c_v[NLen];
	};


	static const VectorType ZERO;
	static const VectorType UNIT_X;
	static const VectorType UNIT_Y;
	static const VectorType UNIT_Z;
	static const VectorType UNIT_SCALE;
};



//Global io
template<typename T>
std::ostream& operator<<(std::ostream& os, const Vector3T<T>& vec);

template<typename T>
std::istream& operator>>(std::istream& is, Vector3T<T>& vec);




END_NAMESPACE_OXGE